/*=============================================================================

  FG HAL MODULE

  FILE: fgHal_met2mc.h
  
  DESCRIPTION

    FG Matrox Meteor-II/MC

  Copyright (c) DaeSung Engineering Inc. All rights reserved.

=============================================================================*/
/*=============================================================================

  EDIT HISTORY

  ---------  -------  -------------------------------------------------------
    when       who       what, where, why
  ---------  -------  -------------------------------------------------------
   07/17/04   nso4th    Initial version

=============================================================================*/

/*=============================================================================
  FEATURE DEFINITION
=============================================================================*/
#define MILLITE_80_ADAPT

/*=============================================================================
  HEADER INCLUSION
=============================================================================*/
#include <OBase.h>
#include <mxBase/fgDev.h>
#include <devSP/dev_mem.h>
#include "fgHal_met2mc.h"
#include <stdio.h>
#include <string.h>

#include <mil.h>
#include <milproto.h>

#ifndef MILLITE_80_ADAPT
	#include <mwinmil.h>
#endif

// LINK LIBS
//  1. MIL-Lite 7.5: mil.lib milmet2d.lib milmet2.lib milvga.lib milvhook.lib 
//  2. MIL-Lite 8.0: mil.lib milmon.lib
//  3. MIL-Lite 9.0: mil.lib milmon.lib

/*=============================================================================
  CONSTANT DEFINITIONS
=============================================================================*/
#define MET2MC_CAM_FILE_LEN			(128)
#define MET2MC_MAX_BUFFER			(2)

#define MET2MC_GRABCONT0_SIG				(0x100)
#define MET2MC_GRABCONT0_STOP_SIG			(0x200)

#define MET2MC_GRABCONT1_SIG				(0x400)
#define MET2MC_GRABCONT1_STOP_SIG			(0x800)

#define MET2MC_NANO_SCALE				(1000000000.0)

#define MET2MC_SYNCWAIT_TIME		(1000) // 1sec.

typedef enum
{
	CAM0 = 0,
	CAM1 = 1,
	CAM_MAX
} MET2MC_CAM;

/*=============================================================================
  STRUCTURES
=============================================================================*/
typedef struct
{
	int32	size;
	int32	start;
	int32	req;
	int32	req_done;
	int32	nEnd;
	MIL_ID	*pBuf;
} met2mc_q_s;

typedef struct
{
	boolean		init;
	dword		dev_id;
	dword		err_code;

	dword		grab_mode[CAM_MAX];
	
	boolean		host_mem[CAM_MAX];
	
	boolean		ddraw[CAM_MAX];
	char			szcam[MET2MC_CAM_FILE_LEN];
	char			devcam[CAM_MAX][MET2MC_CAM_FILE_LEN];
	dword		chan_num[CAM_MAX];
	dword		cam_w[CAM_MAX];
	dword		cam_h[CAM_MAX];
	dword		cam_pitch[CAM_MAX];

	int32		img_thresh[CAM_MAX];
	HWND		hWnd[CAM_MAX];
	
	MIL_ID		mSys;
	MIL_ID		mDig[CAM_MAX];
	MIL_ID		mEvt[CAM_MAX];
	MIL_ID		mDisp[CAM_MAX];
	MIL_ID		mDispBuf[CAM_MAX];
	MIL_ID		mLut[CAM_MAX];
	MIL_ID		mBuf[CAM_MAX][MET2MC_MAX_BUFFER];

	int32		camSEL;
	int32		camNUM;

	int32		async_cnt[CAM_MAX];

	byte			*pData[CAM_MAX][MET2MC_MAX_BUFFER];

	mcxt		ss[CAM_MAX]; // simple sync. object
	boolean		hook_cont[CAM_MAX];
	boolean		sync_lock[CAM_MAX];
	boolean		async_lock[CAM_MAX];

	met2mc_q_s	*que[CAM_MAX];  // For the Further Timing Critical Processing Issue
	mcxt		pObj;
	ObjClient		pCli;
	int32		start_cnt[CAM_MAX];
	int32		end_cnt[CAM_MAX];
} met2mc_s;


/*=============================================================================
  LOCALIZED FUCNTIONS
=============================================================================*/
static FgHal_s	*gHal = NULL;

// HAL Interface
static dword met2mc_Execute(mcxt pObj, dword dwCmd, dword wParam, dword lParam);
static dword met2mc_SetParam(mcxt pObj, dword dwType, dword wParam, dword lParam);
static dword met2mc_GetParam(mcxt pObj, dword dwType, dword wParam, dword lParam);

// Hardware Dependent
static mcxt met2mc_alloc(mcxt pObj, dword dev_id);
static boolean met2mc_free(met2mc_s *dev);
static boolean met2mc_init(met2mc_s *dev);
static boolean met2mc_close(met2mc_s *dev);
static void met2mc_reset(met2mc_s *dev, int32 camID);

static void met2mc_err_reset(met2mc_s *dev);
static void met2mc_err_set(met2mc_s *dev, dword err_code);

static boolean met2mc_grab_sync(met2mc_s *dev, boolean bCont, byte *pBuf);
static boolean met2mc_grab_async(met2mc_s *dev, boolean bCont);
static boolean met2mc_grab_stop(met2mc_s *dev);

// Asynchronous Acquisition Function
// CAM0
static int32 MFTYPE met2mc_async_start0(int32 HookType, MIL_ID EventID, void MPTYPE *pCxt);
static int32 MFTYPE met2mc_async_end0(int32 HookType, MIL_ID EventID, void MPTYPE *pCxt);
static int32 MFTYPE met2mc_async_1start0(int32 HookType, MIL_ID EventID, void MPTYPE *pCxt);
static int32 MFTYPE met2mc_async_1end0(int32 HookType, MIL_ID EventID, void MPTYPE *pCxt);
// CAM1
static int32 MFTYPE met2mc_async_start1(int32 HookType, MIL_ID EventID, void MPTYPE *pCxt);
static int32 MFTYPE met2mc_async_end1(int32 HookType, MIL_ID EventID, void MPTYPE *pCxt);
static int32 MFTYPE met2mc_async_1start1(int32 HookType, MIL_ID EventID, void MPTYPE *pCxt);
static int32 MFTYPE met2mc_async_1end1(int32 HookType, MIL_ID EventID, void MPTYPE *pCxt);

static dword met2mc_async_event(met2mc_s *dev, dword dwCmd);
static void met2mc_que_reset(met2mc_s *dev, int32 camID);

// MIL Application
extern void MIL_Add(void);
extern void MIL_Release(void);
extern MIL_ID MIL_GetApp(void);

// Task function
dword __stdcall met2mc_Task(void *pCxt);
void met2mc_evt_cb(met2mc_s *dev, dword dwEvent, dword wParam, dword lParam);
int32 met2mc_cam_info(char *src, char *dst0, char *dst1); 

/*=============================================================================
  ENUMERATORS
=============================================================================*/

/*=============================================================================
  EXTERNALIZED FUNCION DEFINITION
=============================================================================*/
boolean FgHal_Met2MC_GetVersion(char *pszVer, int32 strLen)
{
	static char	szTmp[64];

	if(!pszVer)
	{
		return	FALSE;
	}
#ifdef _ODEBUG_
	sprintf(szTmp, "fg_met2mcD.%s (%s)", "V04.001", __DATE__);
#else
	sprintf(szTmp, "fg_met2mc.%s (%s)", "V04.001", __DATE__);
#endif
	if(strLen > (int32)strlen(szTmp))
	{
		strcpy(pszVer, szTmp);
		return	TRUE;
	}

	return	FALSE;
}


boolean FgHal_Met2MC_Register(void)
{
	FgHal_s		*pHal = gHal;

	if(pHal == NULL)
	{
		gHal = memCalloc(sizeof(*gHal));
		pHal = gHal;
	}

	pHal->dwDevID = FGDEV_METEOR_II_MC;
	pHal->pfnTaskCb = met2mc_Task;
	pHal->SetParamF = met2mc_SetParam;
	pHal->GetParamF = met2mc_GetParam;
	pHal->ExecuteF = met2mc_Execute;

	//MIL App.
	MIL_Add();
	
	return	FgDev_HalRegister(pHal);
}

boolean FgHal_Met2MC_Deregister(void)
{
	FgHal_s		*pHal = gHal;

	//MIL App.
	MIL_Release();

	if(pHal)
	{
		FgDev_HalDeregister(pHal);
		memFree(pHal);
		pHal = NULL;
		gHal = NULL;
	}
	
	return	FALSE;
}


/*=============================================================================
  HAL INTERFACING FUNCTIONS
=============================================================================*/
dword met2mc_SetParam(mcxt pObj, dword dwType, dword wParam, dword lParam)
{
	met2mc_s		*dev = (met2mc_s*)pObj;
	int32		camID = dev->camSEL;
	
	switch(dwType)
	{
	case FGDEV_ISDEVINIT_P:
		return	FALSE;
		
	case FGDEV_CAM_INFO_P:
	{
		char	*pszcam = (char*)wParam;

		if(dev->init)
			return	FALSE;

		if(strlen(pszcam) < (MET2MC_CAM_FILE_LEN-1))
		{
			strcpy(dev->szcam, pszcam);
			return	TRUE;
		}
		break;
	}
	case FGDEV_WIDTH_P:
	case FGDEV_HEIGHT_P:
	case FGDEV_PITCH_P:
	case FGDEV_CHANNUM_P: //FGDEV_COLOR_P:
	case FGDEV_GRAB_P:
	case FGDEV_GRAB_CONT_P:
	case FGDEV_GRABMODE_P:
		return	FALSE;

	// Extra Parameters
	case FGDEV_HAL_BASE_P:
		return	FALSE;
		
	case FGDEV_MET2MC_DDRAW_P:
	{
		if(dev->grab_mode[camID] != FGDEV_NONE_GRAB)
			return	FALSE;

		dev->ddraw[camID] = (boolean)wParam;
		return	TRUE;
	}
	case FGDEV_MET2MC_HWND_P:
	{
		if(dev->grab_mode[camID] != FGDEV_NONE_GRAB)
			return	FALSE;

		dev->hWnd[camID] = (HWND)wParam;
		return	TRUE;
	}
	case FGDEV_MET2MC_THRESH_P:
	{
		int32		i, img_thresh = (int32)wParam;
		byte		*pLut;

		met2mc_err_reset(dev);
		if(dev->init == FALSE)
		{
			met2mc_err_set(dev, FGDEV_HW_NOTINIT_ERR_E);
			return	FALSE;
		}
		if(dev->grab_mode[camID] != FGDEV_NONE_GRAB)
		{
			met2mc_err_set(dev, FGDEV_HW_PENDING_ERR_E);
			return	FALSE;
		}
		if(dev->img_thresh[camID] == img_thresh)
			return	TRUE;
		
		pLut = (byte*)memCalloc((dword)256);
		if(img_thresh > 0)
		{
			for(i=0; i<(int32)256; i++)
			{
				if(i<img_thresh)
					*(pLut+i) = 0;
				else
					*(pLut+i) = 0xFF;
			}
		}
		else
		{
			for(i=0; i<(int32)256; i++)
			{
				*(pLut+i) = (byte)i;
			}
		}

		if(dev->mLut == 0L)
		{
			MbufAlloc1d(dev->mSys, 256L, 8L+M_UNSIGNED, M_LUT, &(dev->mLut[camID]));
		}
		MbufPut1d(dev->mLut[camID], 0L, 256L, (byte*)pLut);
		MdigLut(dev->mDig[camID], dev->mLut[camID]);

		memFree(pLut);
		dev->img_thresh[camID] = img_thresh;
		
		return	TRUE;
	}
	case FGDEV_MET2MC_CAMSEL_P:
	{
		if((int32)wParam < dev->camNUM)
		{
			dev->camSEL = (int32)wParam;
			return	TRUE;
		}
		break;
	}
	case FGDEV_MET2MC_ASYNCCOUNT_P:
	{
		dev->async_cnt[camID] = (int32)wParam;
		DBGPRINT("[FGDEV_MET2MC_ASYNCCOUNT_P(%d,%d)] %d\n", dev->dev_id, camID, dev->async_cnt[camID]);
		return	TRUE;
		break;
	}
	default:
		break;
	}

	return	FALSE;
}

dword met2mc_GetParam(mcxt pObj, dword dwType, dword wParam, dword lParam)
{
	met2mc_s	*dev = (met2mc_s*)pObj;
	int32	camID;

	camID = dev->camSEL;
	
	switch(dwType)
	{
	case FGDEV_ISDEVINIT_P:
		return	(dword)(dev->init);
		
	case FGDEV_CAM_INFO_P:
		return	(dword)(dev->szcam[camID]);

	case FGDEV_WIDTH_P:
		return	(dword)(dev->cam_w[camID]);
	
	case FGDEV_HEIGHT_P:
		return	(dword)(dev->cam_h[camID]);

	case FGDEV_PITCH_P:
		return	(dword)(dev->cam_pitch[camID]);

	case FGDEV_CHANNUM_P:
		return	(dword)(dev->chan_num[camID]);

	case FGDEV_GRAB_CONT_P:
	case FGDEV_GRAB_P: //FGDEV_GRAB_CONT_P
		if(dev->grab_mode[camID] == FGDEV_NONE_GRAB)
			return	(boolean)FALSE;

		return	(boolean)TRUE;

	case FGDEV_GRABMODE_P:
		return	dev->grab_mode[camID];

	// Extra Parameters
	case FGDEV_MET2MC_DDRAW_P:
		return	(dword)(dev->ddraw[camID]);

	case FGDEV_MET2MC_HWND_P:
		return	(dword)(dev->hWnd[camID]);

	case FGDEV_MET2MC_THRESH_P:
		return	(dword)(dev->img_thresh[camID]);

	case FGDEV_MET2MC_CAMNUM_P:
	{
		return	(dword)dev->camNUM;
	}
	case FGDEV_MET2MC_CAMSEL_P:
	{
		return	(dword)dev->camSEL;
	}
	case FGDEV_MET2MC_ASYNCCOUNT_P:
	{
		return	(dword)dev->async_cnt[camID];
	}
	default:
		break;
	}
	return	FALSE;
}

dword met2mc_Execute(mcxt pObj, dword dwCmd, dword wParam, dword lParam)
{
	switch(dwCmd)
	{
	case FGDEV_ALLOC_C:
		return	(dword)met2mc_alloc((mcxt)wParam, (dword)lParam);

	case FGDEV_FREE_C:
		return	(dword)met2mc_free((met2mc_s*)pObj);

	case FGDEV_INIT_C:
		return	(dword)met2mc_init((met2mc_s*)pObj);

	case FGDEV_LASTERROR_C:
	{
		met2mc_s		*dev = (met2mc_s*)pObj;

		return	dev->err_code;
	}
	case FGDEV_CLOSE_C:
		return	(dword)met2mc_close((met2mc_s*)pObj);

	case FGDEV_GRAB_SYNC_C:
		return	(dword)met2mc_grab_sync((met2mc_s*)pObj, (boolean)wParam, (byte*)lParam);

	case FGDEV_GRAB_ASYNC_C:
		return	(dword)met2mc_grab_async((met2mc_s*)pObj, (boolean)wParam);

	case FGDEV_GRAB_STOP_C:
		return	(dword)met2mc_grab_stop((met2mc_s*)pObj);

	case FGDEV_HAL_BASE_C:
		break;
	case FGDEV_MET2MC_ASYNCEVT_SET_C:
	case FGDEV_MET2MC_ASYNCEVT_RESET_C:
	case FGDEV_MET2MC_ASYNCEVT_WAIT_C:
		return	(dword)met2mc_async_event((met2mc_s*)pObj, dwCmd);
	default:
		break;
	}

	return	(dword)FALSE;
}

/*=============================================================================
  HARDWARE INTERFACING FUNCTIONS
=============================================================================*/
mcxt met2mc_alloc(mcxt pObj, dword dev_id)
{
	met2mc_s		*dev;

	dev = memCalloc(sizeof(*dev));
	dev->pObj = pObj;
	dev->dev_id = dev_id;
	dev->pCli = FgDev_HalGetClient(pObj);

	return	dev;
}

boolean met2mc_free(met2mc_s *dev)
{
	if(dev)
	{
		met2mc_close(dev);
		memFree(dev);
	}

	return	FALSE;
}

boolean met2mc_init(met2mc_s *dev)
{
	int32			i, j;
	dword			dwColorMode;

	met2mc_err_reset(dev);
	
	if(dev->init == TRUE)
		return	TRUE;

	//System (BOARD)
	if(MsysAlloc(M_SYSTEM_METEOR_II, M_DEV0+(dev->dev_id), M_COMPLETE, &(dev->mSys))==M_NULL)
	{
		met2mc_err_set(dev, FGDEV_HW_ERR_E);
		return	FALSE;
	}

	dev->camNUM = met2mc_cam_info((char*)(dev->szcam), &dev->devcam[0][0], &dev->devcam[1][0]); 
	
	if(dev->camNUM <= 0)
	{
		return	FALSE;
	}

	for(j=0; j<dev->camNUM; j++)
	{
		//Camera
		if(MdigAlloc(dev->mSys, M_DEV0+j, (char*)(&dev->devcam[j][0]), M_DEFAULT, &(dev->mDig[j])) == M_NULL)
		{
			met2mc_err_set(dev, FGDEV_HW_ERR_E);
			
			MsysFree(dev->mSys);
			return	FALSE;
		}
		else
		{
			//Image size
			MdigInquire(dev->mDig[j], M_SOURCE_SIZE_X, (void*)(&(dev->cam_w[j])));
			MdigInquire(dev->mDig[j], M_SOURCE_SIZE_Y, (void*)(&(dev->cam_h[j])));

			// Image Channels
			MdigInquire(dev->mDig[j], M_COLOR_MODE, (void*)&dwColorMode);
			switch(dwColorMode)
			{
			case M_EXTERNAL_CHROMINANCE:
				dev->chan_num[j] = 3;
				break;
			case M_RGB :
				dev->chan_num[j] = 3;
				break;
			case M_MONO8_VIA_RGB:
				dev->chan_num[j] = 1;   // <phkim, 1107Y05> Check hardware
				break;
			case M_MONOCHROME:
				dev->chan_num[j] = 1;
				break;

			case M_COMPOSITE:
			default:
				dev->chan_num[j] = 3;
				break;
			}
			MdigControl(dev->mDig[j], M_GRAB_TIMEOUT, M_INFINITE);
			/*
			MdigInquire(dev->mDig, M_GRAB_EXPOSURE_TIME+M_TIMER2, &(dExp));  // in nsec.
			dev->dwExposure = (dword)(MET2MC_NANO_SCALE/(dExp));
			*/
		}

		//Image Buffer
		for(i=0; i<MET2MC_MAX_BUFFER; i++)
		{
			if(dev->chan_num[j] == 3)
			{
				dev->host_mem[j] = FALSE;  //Not support for 3-band color
				if(MbufAllocColor(dev->mSys, 3, dev->cam_w[j], dev->cam_h[j], \
					8L+M_UNSIGNED, M_IMAGE+M_GRAB, &(dev->mBuf[j][i])) == M_NULL)
				{
					MdigFree(dev->mDig[i]);
					MsysFree(dev->mSys);

					met2mc_err_set(dev, FGDEV_HW_ERR_E);
					
					return	FALSE;
				}
			}
			else
			{
				dev->host_mem[i] = FALSE;  // Later, we'll update.
				if(MbufAlloc2d(dev->mSys, dev->cam_w[j], dev->cam_h[j], \
					8L+M_UNSIGNED, M_IMAGE+M_GRAB, &(dev->mBuf[j][i])) == M_NULL)
				{
					MdigFree(dev->mDig[j]);
					MsysFree(dev->mSys);

					met2mc_err_set(dev, FGDEV_HW_ERR_E);
					
					return	FALSE;
				}
			}
			MbufClear(dev->mBuf[j][i], 0x0000L);
		}

		// Display for Direct Draw
		MdispAlloc(dev->mSys, M_DEFAULT, "M_DEFAULT", M_WINDOWED, &(dev->mDisp[j]));
		if(dev->chan_num[j] == 3)
		{
			MbufAllocColor(dev->mSys, 3, dev->cam_w[j], dev->cam_h[j], \
				8L+M_UNSIGNED, M_IMAGE+M_GRAB+M_DISP, &(dev->mDispBuf[j]));
		}
		else
		{
			MbufAlloc2d(dev->mSys, dev->cam_w[j], dev->cam_h[j], \
					8L+M_UNSIGNED, M_IMAGE+M_GRAB+M_DISP, &(dev->mDispBuf[j]));
		}

		//Image info.
		dev->cam_w[j] = MbufInquire(dev->mBuf[j][0], M_SIZE_X, M_NULL);
		dev->cam_h[j] = MbufInquire(dev->mBuf[j][0], M_SIZE_Y, M_NULL);
		dev->cam_pitch[j] = MbufInquire(dev->mBuf[j][0], M_PITCH_BYTE, M_NULL);

		// User Buffer
		if(dev->host_mem[j])
		{
			for(i=0; i<MET2MC_MAX_BUFFER; i++)
			{
				dev->pData[j][i] = (byte*)MbufInquire(dev->mBuf[j][i], M_HOST_ADDRESS, M_NULL);
			}
		}
		else
		{
			dword		memSize;
			
			memSize = dev->cam_w[j] * dev->cam_h[j];
			if(dev->chan_num[j] == 3)
			{
				memSize *= 3;
			}
			
			for(i=0; i<MET2MC_MAX_BUFFER; i++)
			{
				dev->pData[j][i] = memCalloc(memSize);
			}
		}

		// Que definition
		dev->que[j] = memCalloc(sizeof(*(dev->que[j])));
		dev->que[j]->pBuf = &dev->mBuf[j][0];
		met2mc_que_reset(dev, j);

		if(dev->ss[j] == NULL)
		{
			dev->ss[j] = Sync_Alloc();
		}
		
		dev->grab_mode[j] = FGDEV_NONE_GRAB;
	}
	dev->camSEL = 0;
	dev->init = TRUE;
	
	return	TRUE;
}

boolean met2mc_close(met2mc_s *dev)
{
	int32		i, j;

	met2mc_err_reset(dev);
	
	if(!dev->init)
		return	FALSE;

	for(j=0; j<CAM_MAX; j++)
	{
		if(dev->grab_mode[j] != FGDEV_NONE_GRAB)
			met2mc_grab_stop(dev);

		if(dev->que[j])
		{
			memFree(dev->que[j]);
			dev->que[j] = NULL;
		}
		
		if(dev->mLut[j])
		{
			MbufFree(dev->mLut[j]);
			dev->mLut[j] = 0L;
		}
		if(dev->host_mem[j] == FALSE)
		{
			for(i=0; i<MET2MC_MAX_BUFFER; i++)
			{
				if(dev->pData[j][i]) memFree(dev->pData[j][i]);
				dev->pData[j][i] = NULL;
			}
		}

		for(i=0; i<MET2MC_MAX_BUFFER; i++)
		{
			if(dev->mBuf[j][i])
			{
				MbufFree(dev->mBuf[j][i]);
				dev->mBuf[j][i] = 0L;
			}
		}
		if(dev->mDispBuf[j])
		{
			MbufFree(dev->mDispBuf[j]);
			dev->mDispBuf[j] = 0L;
		}

		if(dev->mDisp[j])
		{
			MdispFree(dev->mDisp[j]);
		}
		if(dev->mDig[j])
		{
			MdigFree(dev->mDig[j]);
		}

		dev->mDisp[j] = 0L;
		dev->mDig[j] = 0L;
		
		if(dev->ss[j])
		{
			Sync_Set(dev->ss[j], TRUE);
			Sync_Free(dev->ss[j]);
			dev->ss[j] = NULL;
		}
	}
	MsysFree(dev->mSys);
	
	dev->mSys;
	dev->init = FALSE;

	return	TRUE;
}

void met2mc_reset(met2mc_s *dev, int32 camID)
{
#if 0
	// CLOSE
	MdigFree(dev->mDig[camID]);
	dev->mDig[camID] = 0L;

	//Camera
	if(MdigAlloc(dev->mSys, M_DEV0+camID, (char*)(&dev->devcam[camID][0]), M_DEFAULT, &(dev->mDig[camID])) == M_NULL)
	{
	}
	else
	{
		MdigControl(dev->mDig[camID], M_GRAB_TIMEOUT, M_INFINITE);
	}
#endif
}

void met2mc_err_reset(met2mc_s *dev)
{
	dev->err_code = FGDEV_ERROR_BASE_E;
}

void met2mc_err_set(met2mc_s *dev, dword err_code)
{
	dev->err_code = err_code;
	met2mc_evt_cb(dev, dev->err_code, 0, 0);
}

boolean met2mc_grab_sync(met2mc_s *dev, boolean bCont, byte *pBuf)
{
	int32		camID =dev->camSEL;
	
	met2mc_err_reset(dev);

	if(dev->init == FALSE)
	{
		met2mc_err_set(dev, FGDEV_HW_NOTINIT_ERR_E);
		return	FALSE;
	}
	if(dev->grab_mode[camID] != FGDEV_NONE_GRAB)
	{
		met2mc_err_set(dev, FGDEV_HW_PENDING_ERR_E);
		return	FALSE;
	}

	MdigControl(dev->mDig[camID], M_GRAB_MODE, M_SYNCHRONOUS);
	
	// Display
	if(dev->ddraw[camID] && IsWindow(dev->hWnd[camID]) && bCont)
	{
		dev->grab_mode[camID] = FGDEV_SYNC_CONT_GRAB;
		MbufClear(dev->mDispBuf[camID], 0x00000000L);
		MdispSelectWindow(dev->mDisp[camID], dev->mDispBuf[camID], dev->hWnd[camID]);
		MdigGrabContinuous(dev->mDig[camID], dev->mDispBuf[camID]);
	}
	else
	{
		if(bCont)
		{
			dev->grab_mode[camID] = FGDEV_SYNC_CONT_GRAB;
			if(camID == 0)
			{
				Task_PutSignal(FgDev_HalGetTCB(dev->pObj), MET2MC_GRABCONT0_SIG);
			}
			else
			{
				Task_PutSignal(FgDev_HalGetTCB(dev->pObj), MET2MC_GRABCONT1_SIG);
			}
		}
		else
		{	
			if(pBuf == NULL)
			{
				met2mc_err_set(dev, FGDEV_HW_INVALID_PARAM_ERR_E);
				return	FALSE;
			}

			dev->grab_mode[camID] = FGDEV_SYNC_GRAB;
			MdigGrab(dev->mDig[camID], dev->mBuf[camID][0]);

			MbufGet(dev->mBuf[camID][0], pBuf);
			dev->grab_mode[camID] = FGDEV_NONE_GRAB;
		}
	}

	return	TRUE;
}

boolean met2mc_grab_async(met2mc_s *dev, boolean bCont)
{
	int32		camID =dev->camSEL;
	met2mc_q_s	*q = dev->que[camID];

	met2mc_err_reset(dev);
	
	if(dev->init == FALSE)
	{
		met2mc_err_set(dev, FGDEV_HW_NOTINIT_ERR_E);
		return	FALSE;
	}
	if(dev->grab_mode[camID] != FGDEV_NONE_GRAB)
	{
		met2mc_err_set(dev, FGDEV_HW_PENDING_ERR_E);
		return	FALSE;
	}

	met2mc_que_reset(dev, camID);
	MdigControl(dev->mDig[camID], M_GRAB_MODE, M_ASYNCHRONOUS);

	if(dev->mEvt[camID])
	{
		if(dev->hook_cont)
		{
			if(camID == 0)
			{
				MdigHookFunction(dev->mDig[camID], M_GRAB_START+M_UNHOOK, met2mc_async_start0, (void*)dev);
				MdigHookFunction(dev->mDig[camID], M_GRAB_END+M_UNHOOK, met2mc_async_end0, (void*)dev);
			}
			else
			{
				MdigHookFunction(dev->mDig[camID], M_GRAB_START+M_UNHOOK, met2mc_async_start1, (void*)dev);
				MdigHookFunction(dev->mDig[camID], M_GRAB_END+M_UNHOOK, met2mc_async_end1, (void*)dev);
			}
		}
		else
		{
			if(camID == 0)
			{
				MdigHookFunction(dev->mDig[camID], M_GRAB_START+M_UNHOOK, met2mc_async_1start0, (void*)dev);
				MdigHookFunction(dev->mDig[camID], M_GRAB_END+M_UNHOOK, met2mc_async_1end0, (void*)dev);
			}
			else
			{
				MdigHookFunction(dev->mDig[camID], M_GRAB_START+M_UNHOOK, met2mc_async_1start1, (void*)dev);
				MdigHookFunction(dev->mDig[camID], M_GRAB_END+M_UNHOOK, met2mc_async_1end1, (void*)dev);
			}
		}
		MappControlThread(dev->mEvt[camID], M_EVENT_FREE, M_DEFAULT, M_NULL);
		dev->mEvt[camID] = 0L;
	}

	dev->async_lock[camID] = FALSE;
	dev->async_cnt[camID] = 0;

	if(bCont)
	{	
		dev->grab_mode[camID] = FGDEV_ASYNC_CONT_GRAB;
		if(camID == 0)
		{
			MdigHookFunction(dev->mDig[camID], M_GRAB_START, met2mc_async_start0, (void*)dev);
			MdigHookFunction(dev->mDig[camID], M_GRAB_END, met2mc_async_end0, (void*)dev);
		}
		else
		{
			MdigHookFunction(dev->mDig[camID], M_GRAB_START, met2mc_async_start1, (void*)dev);
			MdigHookFunction(dev->mDig[camID], M_GRAB_END, met2mc_async_end1, (void*)dev);
		}
		MappControlThread(M_DEFAULT, M_EVENT_ALLOC, M_NOT_SIGNALED+M_AUTO_RESET, &(dev->mEvt[camID]));
		dev->hook_cont[camID] = TRUE;
		
		MdigGrab(dev->mDig[camID], *(q->pBuf+q->req));

		dev->start_cnt[camID] = 0;
		dev->end_cnt[camID] = 0;
	}
	else
	{
		dev->grab_mode[camID] = FGDEV_ASYNC_GRAB;
		if(camID == 0)
		{
			MdigHookFunction(dev->mDig[camID], M_GRAB_START, met2mc_async_1start0, (void*)dev);
			MdigHookFunction(dev->mDig[camID], M_GRAB_END, met2mc_async_1end0, (void*)dev);
		}
		else
		{
			MdigHookFunction(dev->mDig[camID], M_GRAB_START, met2mc_async_1start1, (void*)dev);
			MdigHookFunction(dev->mDig[camID], M_GRAB_END, met2mc_async_1end1, (void*)dev);
		}
		MappControlThread(M_DEFAULT, M_EVENT_ALLOC, M_NOT_SIGNALED+M_AUTO_RESET, &(dev->mEvt[camID]));
		dev->hook_cont[camID] = FALSE;
		
		MdigGrab(dev->mDig[camID], *(q->pBuf+q->req));
	}

	return	TRUE;
}

boolean met2mc_grab_stop(met2mc_s *dev)
{
	int32		camID =dev->camSEL;
	int32		i;

	met2mc_err_reset(dev);
	
	switch(dev->grab_mode[camID])
	{
	case FGDEV_SYNC_GRAB:
	{
		MdigControl(dev->mDig[camID], M_GRAB_ABORT, M_DEFAULT); //MdigHalt(dev->mDig);
		
		dev->grab_mode[camID] = FGDEV_NONE_GRAB;
		met2mc_evt_cb(dev, FGDEV_GRABSTOP_E, camID, 0);
		break;
	}
	case FGDEV_ASYNC_GRAB:
	{
		dev->async_lock[camID] = TRUE;
		MdigControl(dev->mDig[camID], M_GRAB_ABORT, M_DEFAULT); //MdigHalt(dev->mDig);

		if(dev->mEvt[camID])
		{
			if(camID == 0)
			{
				MdigHookFunction(dev->mDig[camID], M_GRAB_START+M_UNHOOK, met2mc_async_1start0, (void*)dev);
				MdigHookFunction(dev->mDig[camID], M_GRAB_END+M_UNHOOK, met2mc_async_1end0, (void*)dev);
			}
			else
			{
				MdigHookFunction(dev->mDig[camID], M_GRAB_START+M_UNHOOK, met2mc_async_1start1, (void*)dev);
				MdigHookFunction(dev->mDig[camID], M_GRAB_END+M_UNHOOK, met2mc_async_1end1, (void*)dev);
			}	
			MappControlThread(dev->mEvt[camID], M_EVENT_SET, M_SIGNALED, M_NULL);
			MappControlThread(dev->mEvt[camID], M_EVENT_FREE, M_DEFAULT, M_NULL);
			dev->mEvt[camID] = 0L;
		}
		dev->async_lock[camID] = FALSE;

		dev->grab_mode[camID] = FGDEV_NONE_GRAB;
		met2mc_evt_cb(dev, FGDEV_GRABSTOP_E, camID, 0);

		met2mc_reset(dev, camID);
		break;
	}
	case FGDEV_SYNC_CONT_GRAB:
	{
		dev->sync_lock[camID] = TRUE;
		Sync_Set(dev->ss[camID], FALSE);

		if(camID == 0)
		{
			Task_PutSignal(FgDev_HalGetTCB(dev->pObj), MET2MC_GRABCONT0_STOP_SIG);
		}
		else
		{
			Task_PutSignal(FgDev_HalGetTCB(dev->pObj), MET2MC_GRABCONT1_STOP_SIG);
		}
		if(dev->ddraw[camID])
		{
			MdispDeselect(dev->mDisp[camID], dev->mDispBuf[camID]);
			MdigControl(dev->mDig[camID], M_GRAB_ABORT, M_DEFAULT);
		}
		else
		{
			MdigControl(dev->mDig[camID], M_GRAB_ABORT, M_DEFAULT);
			dev->grab_mode[camID] = FGDEV_NONE_GRAB;
		}
		Sync_Wait(dev->ss[camID], MET2MC_SYNCWAIT_TIME);
		met2mc_evt_cb(dev, FGDEV_GRABSTOP_E, camID, 0);
		break;
	}
	case FGDEV_ASYNC_CONT_GRAB:
	{
		dev->async_lock[camID] = TRUE;
		MdigControl(dev->mDig[camID], M_GRAB_ABORT, M_DEFAULT);
		if(dev->mEvt[camID])
		{
			if(camID == 0)
			{
				MdigHookFunction(dev->mDig[camID], M_GRAB_START+M_UNHOOK, met2mc_async_start0, (void*)dev);
				MdigHookFunction(dev->mDig[camID], M_GRAB_END+M_UNHOOK, met2mc_async_end0, (void*)dev);
			}
			else
			{
				MdigHookFunction(dev->mDig[camID], M_GRAB_START+M_UNHOOK, met2mc_async_start1, (void*)dev);
				MdigHookFunction(dev->mDig[camID], M_GRAB_END+M_UNHOOK, met2mc_async_end1, (void*)dev);
			}	
			MappControlThread(dev->mEvt[camID], M_EVENT_SET, M_SIGNALED, M_NULL);
			MappControlThread(dev->mEvt[camID], M_EVENT_FREE, M_DEFAULT, M_NULL);
			dev->mEvt[camID] = 0L;
		}
		DBGPRINT("\n[GRAB_ASYNC_CONT_DONE] start_cnt = %d, end_cnt = %d\n", dev->start_cnt[camID], dev->end_cnt[camID]);
		dev->async_lock[camID] = FALSE;

		dev->grab_mode[camID] = FGDEV_NONE_GRAB;
		met2mc_evt_cb(dev, FGDEV_GRABSTOP_E, (dword)camID, 0);

		met2mc_reset(dev, camID);
		break;
	}
	case FGDEV_NONE_GRAB:
	default:
		return	TRUE;
	}

	for(i=0; i<MET2MC_MAX_BUFFER; i++)
	{
		MbufClear(dev->mBuf[camID][i], 0x00000000L);
	}
	
	return	TRUE;
}


// Asynchronous Acquisition Function
// CAM 0
int32 MFTYPE 	met2mc_async_start0(int32 HookType, MIL_ID EventID, void MPTYPE *pCxt)
{
	met2mc_s		*dev = (met2mc_s*)pCxt;
	met2mc_q_s	*q = (dev->que[CAM0]);
	
	dev->start_cnt[CAM0] += 1;
	met2mc_evt_cb(dev, FGDEV_GRAB_START_E, CAM0, 0);

	//DBGPRINT("[ASYNC_START(%d)] %d (%d) => %d <<%d>>", \
	//				dev->dev_id, dev->start_cnt[CAM0], \
	//				dev->async_cnt[CAM0], \
	//				dev->end_cnt[CAM0], \
	//				q->req);
	
	if((dev->async_lock[CAM0] == FALSE) \
		&& ((dev->async_cnt[CAM0] <= 0) || (dev->start_cnt[CAM0] < dev->async_cnt[CAM0])))
	{
		DBGPRINT("......");
		q->req = (q->req+1)%(q->size);
		MdigGrab(dev->mDig[CAM0], *(q->pBuf + q->req));
	}
	else
	{
		DBGPRINT("==> X\n");
	}
	
	return	0;
}

int32 MFTYPE met2mc_async_end0(int32 HookType, MIL_ID EventID, void MPTYPE *pCxt)
{
	met2mc_s		*dev = (met2mc_s*)pCxt;
	met2mc_q_s	*q = (dev->que[CAM0]);

	dev->end_cnt[CAM0] += 1;
	q->req_done = (q->req_done + 1)%(q->size);

	//DBGPRINT("[ASYNC_END(%d)] %d (%d) => %d <<%d>>\n", \
	//				dev->dev_id, dev->start_cnt[CAM0], \
	//				dev->async_cnt[CAM0], \
	//				dev->end_cnt[CAM0],\
	//				q->req_done);
	
	MbufGet(dev->mBuf[CAM0][q->req_done], dev->pData[CAM0][q->req_done]);
	met2mc_evt_cb(dev, FGDEV_IMAGE_E, (dword)(dev->pData[CAM0][q->req_done]), CAM0);
			
	return	0;
}

int32 MFTYPE met2mc_async_1start0(int32 HookType, MIL_ID EventID, void MPTYPE *pCxt)
{
	met2mc_s			*dev = (met2mc_s*)pCxt;


	met2mc_evt_cb(dev, FGDEV_GRAB_START_E, CAM0, 0);
	
#ifdef _ODEBUG_
	DBGPRINT("[met2mc_aSync_1start] Grab Start. \n");
#endif
	return	0;
}

int32 MFTYPE met2mc_async_1end0(int32 HookType, MIL_ID EventID, void MPTYPE *pCxt)
{
	met2mc_s		*dev = (met2mc_s*)pCxt;
	met2mc_q_s	*q = (dev->que[CAM0]);

#ifdef _ODEBUG_
	DBGPRINT("[met2mc_async_1end] Grab Done. \n");
#endif

	q->req_done = (q->req_done + 1)%(q->size);

	MbufGet(dev->mBuf[0][q->req_done], dev->pData[0][q->req_done]);
	met2mc_evt_cb(dev, FGDEV_IMAGE_E, (dword)(dev->pData[0][q->req_done]), CAM0);

	dev->grab_mode[0] = FGDEV_NONE_GRAB;
	met2mc_evt_cb(dev, FGDEV_GRABSTOP_E, CAM0, 0);

	{
		dword		memSize;
		
		memSize = dev->cam_w[CAM0] * dev->cam_h[CAM0];
		if(dev->chan_num[CAM0] == 3)
		{
			memSize *= 3;
		}
		memZeros(dev->pData[0][q->req_done], memSize);
	}
	
	return	0;
}

// CAM 2
int32 MFTYPE 	met2mc_async_start1(int32 HookType, MIL_ID EventID, void MPTYPE *pCxt)
{
	met2mc_s		*dev = (met2mc_s*)pCxt;
	met2mc_q_s	*q = (dev->que[CAM1]);
	
	dev->start_cnt[CAM1] += 1;
	met2mc_evt_cb(dev, FGDEV_GRAB_START_E, CAM1, 0);

	if((dev->async_lock[CAM1] == FALSE) \
		&& ((dev->async_cnt[CAM1] <= 0) || (dev->start_cnt[CAM1] < dev->async_cnt[CAM1])))
	{
		q->req = (q->req+1)%(q->size);
		MdigGrab(dev->mDig[CAM1], *(q->pBuf + q->req));
	}
	
	return	0;
}

int32 MFTYPE met2mc_async_end1(int32 HookType, MIL_ID EventID, void MPTYPE *pCxt)
{
	met2mc_s		*dev = (met2mc_s*)pCxt;
	met2mc_q_s	*q = (dev->que[CAM1]);


	dev->end_cnt[CAM1] += 1;
	q->req_done = (q->req_done + 1)%(q->size);

	MbufGet(dev->mBuf[CAM1][q->req_done], dev->pData[1][q->req_done]);
	met2mc_evt_cb(dev, FGDEV_IMAGE_E, (dword)(dev->pData[1][q->req_done]), CAM1);
			
	return	0;
}

int32 MFTYPE met2mc_async_1start1(int32 HookType, MIL_ID EventID, void MPTYPE *pCxt)
{
	met2mc_s			*dev = (met2mc_s*)pCxt;


	met2mc_evt_cb(dev, FGDEV_GRAB_START_E, CAM1, 0);	
	DBGPRINT("[met2mc_aSync_1start] Grab Start. \n");
	
	return	0;
}

int32 MFTYPE met2mc_async_1end1(int32 HookType, MIL_ID EventID, void MPTYPE *pCxt)
{
	met2mc_s		*dev = (met2mc_s*)pCxt;
	met2mc_q_s	*q = (dev->que[CAM1]);

	DBGPRINT("[met2mc_async_1end] Grab Done. \n");
	q->req_done = (q->req_done + 1)%(q->size);

	MbufGet(dev->mBuf[CAM1][q->req_done], dev->pData[CAM1][q->req_done]);
	met2mc_evt_cb(dev, FGDEV_IMAGE_E, (dword)(dev->pData[CAM1][q->req_done]), CAM1);

	dev->grab_mode[CAM1] = FGDEV_NONE_GRAB;
	met2mc_evt_cb(dev, FGDEV_GRABSTOP_E, CAM1, 0);
	
	return	0;
}


dword met2mc_async_event(met2mc_s *dev, dword dwCmd)
{
	int32		camID = dev->camSEL;
	
	met2mc_err_reset(dev);
	
	if(dev->mEvt[camID] == 0L)
	{
		return	TRUE;
	}
	
	switch(dwCmd)
	{
	case FGDEV_MET2MC_ASYNCEVT_SET_C:
	{
		MappControlThread(dev->mEvt[camID], M_EVENT_SET, M_SIGNALED, M_NULL);
		break;
	}
	case FGDEV_MET2MC_ASYNCEVT_RESET_C:
	{
		MappControlThread(dev->mEvt[camID], M_EVENT_SET, M_NOT_SIGNALED, M_NULL);
		break;
	}
	case FGDEV_MET2MC_ASYNCEVT_WAIT_C:
	{
		MappControlThread(dev->mEvt[camID], M_EVENT_WAIT, M_DEFAULT, M_NULL);
		break;
	}
	default:
	{
		met2mc_err_set(dev, FGDEV_HW_INVALID_PARAM_ERR_E);
		return	FALSE;
	}
	}
	return	TRUE;
}


void met2mc_que_reset(met2mc_s *dev, int32 camID)
{
	met2mc_q_s	*q;

	q = dev->que[camID];
	q->size = MET2MC_MAX_BUFFER;
	q->start = q->nEnd = 0;
	q->req = 0;
	q->req_done = -1;
}

//---------------------------------------------------------
// MIL APPICATION
//---------------------------------------------------------
static MIL_ID		gMILApp = 0L;
static int32		gMILCnt = -1;
void MIL_Add(void)
{
	gMILCnt ++;
	if(gMILCnt == 0)
	{
		if(gMILApp == 0L)
		{
			if(MappAlloc(M_DEFAULT+M_QUIET, &(gMILApp)) == M_NULL)
			{
				return;
			}
		}
#ifdef _ODEBUG_
		MappControl(M_ERROR, M_PRINT_ENABLE);
#else
		MappControl(M_ERROR, M_PRINT_DISABLE);
#endif
	}
}

void MIL_Release(void)
{
	if(gMILCnt == 0)
	{
		if(gMILApp != 0L)
		{
			MappFree(gMILApp);
			gMILApp = 0L;
		}
	}
	gMILCnt--;
}

MIL_ID MIL_GetApp(void)
{
	return	gMILApp;
}

//---------------------------------------------------------
// TASK
//---------------------------------------------------------
dword __stdcall met2mc_Task(void *pCxt)
{
	met2mc_s	*dev = (met2mc_s*)FgDev_HalGetDevice(pCxt);
	mcxt		pTask = FgDev_HalGetTCB(pCxt);
	dword		dwSig;

	while(TRUE)
	{
		dwSig = Task_GetSignal(pTask);
		if(TASK_IS_STARTREQ(dwSig))
		{
			Task_StartACK(pTask);
		}
		if(TASK_IS_STOPREQ(dwSig))
		{
			Task_StopACK(pTask);
			break;
		}
		// CAM0
		if(TASK_SIGNAL_ENABLED(dwSig, MET2MC_GRABCONT0_SIG))
		{
			if(dev->grab_mode[CAM0] == FGDEV_SYNC_CONT_GRAB)
			{
				met2mc_evt_cb(dev, FGDEV_GRAB_START_E, CAM0, 0);
				MdigGrab(dev->mDig[CAM0], dev->mBuf[CAM0][0]);
				if(dev->grab_mode[CAM0] == FGDEV_SYNC_CONT_GRAB)
				{
					MbufGet(dev->mBuf[CAM0][0], dev->pData[CAM0][0]);
					met2mc_evt_cb(dev, FGDEV_IMAGE_E, (dword)(dev->pData[CAM0][0]), CAM0);
				}
				else
				{
					TASK_CLEAR_SIGNAL(pTask, MET2MC_GRABCONT0_SIG);
				}
			}
			else
			{
				TASK_CLEAR_SIGNAL(pTask, MET2MC_GRABCONT0_SIG);
			}
		}
		// CAM1
		if(TASK_SIGNAL_ENABLED(dwSig, MET2MC_GRABCONT0_STOP_SIG))
		{
			if(dev->sync_lock[CAM0] == TRUE)
			{
				dev->sync_lock[CAM0] = FALSE;
				Sync_Set(dev->ss, TRUE);
				TASK_CLEAR_SIGNAL(pTask, MET2MC_GRABCONT0_SIG);
			}
			TASK_CLEAR_SIGNAL(pTask, MET2MC_GRABCONT0_STOP_SIG);
		}
		if(TASK_SIGNAL_ENABLED(dwSig, MET2MC_GRABCONT1_SIG))
		{
			if(dev->grab_mode[CAM1] == FGDEV_SYNC_CONT_GRAB)
			{
				met2mc_evt_cb(dev, FGDEV_GRAB_START_E, CAM1, 0);
				MdigGrab(dev->mDig[CAM1], dev->mBuf[CAM1][0]);
				if(dev->grab_mode[CAM1] == FGDEV_SYNC_CONT_GRAB)
				{
					MbufGet(dev->mBuf[CAM1][0], dev->pData[CAM1][0]);
					met2mc_evt_cb(dev, FGDEV_IMAGE_E, (dword)(dev->pData[CAM1][0]), CAM1);
				}
				else
				{
					TASK_CLEAR_SIGNAL(pTask, MET2MC_GRABCONT1_SIG);
				}
			}
			else
			{
				TASK_CLEAR_SIGNAL(pTask, MET2MC_GRABCONT1_SIG);
			}
		}
		if(TASK_SIGNAL_ENABLED(dwSig, MET2MC_GRABCONT1_STOP_SIG))
		{
			if(dev->sync_lock[CAM1] == TRUE)
			{
				dev->sync_lock[CAM1] = FALSE;
				Sync_Set(dev->ss, TRUE);
				TASK_CLEAR_SIGNAL(pTask, MET2MC_GRABCONT1_SIG);
			}
			TASK_CLEAR_SIGNAL(pTask, MET2MC_GRABCONT1_STOP_SIG);
		}
	}

	return	(1);
}

void met2mc_evt_cb(met2mc_s *dev, dword dwEvent, dword wParam, dword lParam)
{
	if(dev->pCli && dev->pCli->EventCbf)
	{
		dev->pCli->EventCbf(dwEvent, wParam, lParam, dev->pCli->pUserCxt);
	}
}

int32 met2mc_cam_info(char *src, char *dst0, char *dst1)
{
	int32		n, j, k, camNum;
	char			*ptr, ch;
	
	if(src == NULL)
		return	(0);

	n = strlen(src);

	ptr = dst0;
	camNum = 1;
	for(k=0,j=0; j<n; j++)
	{
		ch = *(src+j);
		if(ch == ',')
		{
			k = 0;
			ptr = dst1;
			camNum+=1;
			if(camNum > 2)
			{
				break;
			}
		}
		else
		{
			*(ptr+k) = ch;
			k++;
		}
	}


	return	camNum;
}

/*--- END OF fgHal_met2mc.c ---*/
